package aes.server;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.Timer;

import common.*;
import ocsf.server.*;

/**
 * This class represents AES server logic
 * 
 * @see This class contains main entry point
 *
 */
public class Server extends ObservableServer implements ActionListener {

	/**
	 * Default listening port
	 */
	public static final int DEFAULT_PORT=5555;
	private Timer timer;

	/**
	 * Constructor
	 * @param port
	 */
	public Server(int port) {
		super(port);
		// Initialize timer.
		timer=new Timer(60000, this);
		timer.start();
	}

	/**
	 * Received message handler
	 */
	protected synchronized void handleMessageFromClient(Object message, ConnectionToClient client) {
		setChanged();
		if (message instanceof CommandMessage) {
			notifyObservers(((CommandMessage)message).ToDebugMessage());
			// Handle message
			try {
				AbstractMessage ret = ParseCommandMessage((CommandMessage)message);

				if ((ret instanceof CommandMessage) && ((CommandMessage)ret).getParams()[0].equals(CommandMessage.LOGIN_MESSAGE)) {
					// Log out all clients with the same id.
					User u = (User)((CommandMessage)ret).getParams()[1];
					if (u != null && u.getId() > 0) {
						for (Thread con : getClientConnections()) {
							Object info = ((ConnectionToClient)con).getInfo("UserID");
							if (info != null) {
								int id = Integer.parseInt(info.toString());
								if (id == u.getId()) {
									((ConnectionToClient)con).sendToClient(new CommandMessage(CommandMessage.LOGOUT_USER, null));
								}
								((ConnectionToClient)con).setInfo("UserID", null);
							}
						}
						client.setInfo("UserID", u.getId());
					}
				}
				else if ((ret instanceof CommandMessage) && ((CommandMessage)ret).getParams()[0].equals(CommandMessage.LOCK_EXAM_EXECUTION)) {
					// Inform all connected clients.
					boolean res = Boolean.parseBoolean(((CommandMessage)ret).getParams()[1].toString());
					if (res) {
						// Exam was locked. Need to update users.
						for (Thread con : getClientConnections()) {
							((ConnectionToClient)con).sendToClient(new CommandMessage(CommandMessage.LOCK_EXAM_EXECUTION, ((CommandMessage)message).getParams()));
						}
					}
					ExamExecutionManager.checkExams();
				}
				else if ((ret instanceof CommandMessage) && ((CommandMessage)ret).getParams()[0].equals(CommandMessage.ACCEPT_OR_REJECT_ADDITIONAL_TIME)) {
					// Inform all connected clients.
					int res = Integer.parseInt(((CommandMessage)ret).getParams()[1].toString());
					boolean changed = Boolean.parseBoolean(((CommandMessage)message).getParams()[1].toString());
					if (res > 0 && changed) {
						// Exam time was changed. Need to update users.
						for (Thread con : getClientConnections()) {
							((ConnectionToClient)con).sendToClient(new CommandMessage(CommandMessage.ACCEPT_OR_REJECT_ADDITIONAL_TIME, new Object[] { res, ((CommandMessage)message).getParams()[0], ((CommandMessage)message).getParams()[1], ((CommandMessage)ret).getParams()[3]}));
						}
					}
				}
				client.sendToClient(ret);

				if ((ret instanceof CommandMessage) && ((CommandMessage)ret).getParams()[0].equals(CommandMessage.LOGOUT_USER)) {
					client.setInfo("UserID", null);
				}
			} catch (IOException e) {
				notifyObservers("Can't send message to client: " + client.getInetAddress().toString());
			}
		}
		else
			notifyObservers(message);
	}

	/**
	 * Parse received command message and generate reply message
	 * @param message Received command message and type
	 * @return Reply message
	 */
	private AbstractMessage ParseCommandMessage(CommandMessage txt) {
		if(txt.getCommand().equals(CommandMessage.LOGIN_MESSAGE)) {
			//Check Login and reply
			User u=UserManager.Login((String)txt.getParams()[0],(String)txt.getParams()[1]);

			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE,new Object[] {CommandMessage.LOGIN_MESSAGE,u });
		}
		else if(txt.getCommand().equals(CommandMessage.CREATE_USER)) {
			//Check Login and reply
			int res = UserManager.AddUser((String)txt.getParams()[0],(String)txt.getParams()[1],(String)txt.getParams()[2],(String)txt.getParams()[3],(String)txt.getParams()[4]);

			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE,new Object[] {CommandMessage.CREATE_USER, res});
		}
		else if(txt.getCommand().equals(CommandMessage.LOGOUT_USER)) {
			// Logout.
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE,new Object[] {CommandMessage.LOGOUT_USER});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_QUESTIONS)) {
			// Get questions.
			Question[] questions = null;
			if (txt.getParams()[0] != null && ((String)txt.getParams()[0]).equals("TEACHER")) {
				// Teacher's questions.
				questions = QuestionsManager.getTeacherQuestions(Integer.parseInt((String) txt.getParams()[1]));
			}
			if (txt.getParams()[0] != null && ((String)txt.getParams()[0]).equals("PRINCIPAL")) {
				// Teacher's questions.
				questions = QuestionsManager.getTeachersQuestions();
			}
			else if (txt.getParams()[0] != null && ((String)txt.getParams()[0]).equals("EXAM")) {
				// Exam's questions.
				questions = QuestionsManager.getExamQuestions(Integer.parseInt((String) txt.getParams()[1]));
			}
			else {
				// All questions
				questions = QuestionsManager.getQuestionList();
			}
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_QUESTIONS, txt.getParams()[0], questions});
		}
		else if(txt.getCommand().equals(CommandMessage.DELETE_QUESTION)) {
			// Delete question.
			boolean res = QuestionsManager.removeQuestion((Integer)txt.getParams()[0]);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.DELETE_QUESTION, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_EXAMS)) {
			Object param1 = null;
			Object param2 = null;
			if (txt.getParams()[0] != null)
				param1 = txt.getParams()[0];
			if (txt.getParams()[1] != null)
				param2 = txt.getParams()[1];
			// Get exams.
			Exam[] exams = null;
			if (param1 != null && ((String)param1).equals("TEACHER")) {
				// Teacher's exams.
				exams = ExamsManager.getExamsByTeacher(Integer.parseInt((String)param2));
			}
			else if (param1 != null && ((String)param1).equals("PRINCIPAL")) {
				// All teachers exams.
				exams = ExamsManager.getExams();
			}
			else if (param1 != null && ((String)param1).equals("ID")) {
				exams = new Exam[1];
				exams[0] = ExamsManager.getExam(Integer.parseInt((String)param2));
			}
			else {
				// All exams
				exams = ExamsManager.getExamsList();
			}
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAMS, param1, param2, exams});
		}
		else if(txt.getCommand().equals(CommandMessage.DELETE_EXAM)) {
			// Delete exam.
			boolean res = ExamsManager.removeExam((Integer)txt.getParams()[0]);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.DELETE_EXAM, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_COURSES)) {
			// Get courses list.
			Course[] courses = CoursesManager.getCoursesList();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_COURSES, courses});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_SUBJECTS)) {
			// Get subjects list.
			Subject[] subjects = SubjectsManager.getSubjects();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_SUBJECTS, subjects});
		}
		else if(txt.getCommand().equals(CommandMessage.ADD_QUESTION)) {
			// Add new question.
			Question q = (Question)txt.getParams()[0];
			boolean res = QuestionsManager.addQuestion(q);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ADD_QUESTION, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.EDIT_QUESTION)) {
			// Update question.
			Question q = (Question)txt.getParams()[0];
			boolean res = QuestionsManager.updateQuestion(q);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.EDIT_QUESTION, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_QUESTION)) {
			// Get question.
			int id = Integer.parseInt(txt.getParams()[0].toString());
			Question res = QuestionsManager.getQuestion(id);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_QUESTION,res});
		}
		else if(txt.getCommand().equals(CommandMessage.ADD_EXAM)) {
			// Add new exam.
			Exam e = (Exam)txt.getParams()[0];
			boolean res = ExamsManager.addExam(e);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ADD_EXAM, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.EDIT_EXAM)) {
			// Update exam.
			Exam e = (Exam)txt.getParams()[0];
			boolean res = ExamsManager.updateExam(e);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.EDIT_EXAM, txt.getParams()[0], res});
		}
		else if(txt.getCommand().equals(CommandMessage.ADD_QUESTION_TO_EXAM)) {
			// Add question to exam.
			if (txt.getParams().length<2)
				return null;
			int eId = Integer.parseInt(txt.getParams()[0].toString());
			Question q = (Question)txt.getParams()[1];
			boolean res = ExamsManager.addQuestionToExam(q, eId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ADD_QUESTION_TO_EXAM, txt.getParams()[0], txt.getParams()[1], res});
		}
		else if(txt.getCommand().equals(CommandMessage.REMOVE_QUESTION_FROM_EXAM)) {
			// Remove question from exam.
			if (txt.getParams().length<2)
				return null;
			int eId = Integer.parseInt(txt.getParams()[0].toString());
			int qId = Integer.parseInt(txt.getParams()[1].toString());
			boolean res = ExamsManager.removeQuestionFromExam(qId, eId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.REMOVE_QUESTION_FROM_EXAM, txt.getParams()[0], txt.getParams()[1], res});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_EXAM_NOT_EXISTENT_QUESTIONS)) {
			// Retrieve exam's not existent questions.
			if (txt.getParams().length<1)
				return null;
			int eId = Integer.parseInt(txt.getParams()[0].toString());
			Question[] res = ExamsManager.getNotExistentQuestions(eId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAM_NOT_EXISTENT_QUESTIONS, res});
		}
		else if(txt.getCommand().equals(CommandMessage.GET_EXAMS_BY_SUBJECT_COURSE)) {
			// Retrieve exam by course id and subject id.
			int sId = Integer.parseInt(txt.getParams()[0].toString());
			int cId = Integer.parseInt(txt.getParams()[1].toString());
			Integer[] res = ExamsManager.getExamsIDsList(cId, sId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAMS_BY_SUBJECT_COURSE, res});
		}/////////
		else if(txt.getCommand().equals(CommandMessage.GET_EXAMS_CODE_WHICH_TIME_CHANGED)) {
			// Retrieve exams code which time teacher want to change.
			String[] res = ExamExecutionManager.getCodeListOfTimeChancges();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAMS_CODE_WHICH_TIME_CHANGED, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_NOTES_BY_CODE)) {
			// Retrieve notes by execution code.
			String code = txt.getParams()[0].toString();
			String res = ExamExecutionManager.getNotes(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_NOTES_BY_CODE, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_ADDITIONAL_TIME_BY_CODE)) {
			// Retrieve additional time by execution code.
			String code = txt.getParams()[0].toString();
			int res = ExamExecutionManager.getAditionalTime(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_ADDITIONAL_TIME_BY_CODE, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.ACCEPT_OR_REJECT_ADDITIONAL_TIME)) {
			// Accept or reject additional time to exam execution.
			String code = txt.getParams()[0].toString();
			boolean  accepted = (Boolean) txt.getParams()[1];
			int[] teacherId = new int[]{0};
			int res = ExamExecutionManager.responseTimeChange(code, accepted, teacherId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ACCEPT_OR_REJECT_ADDITIONAL_TIME, res, code, teacherId[0]});
		}///////
		else if(txt.getCommand().equals(CommandMessage.REQUEST_ADDITIONAL_TIME)) {
			// Request additional time to exam execution.
			String code = txt.getParams()[0].toString();
			int time =  (Integer) txt.getParams()[1];
			String notes = txt.getParams()[2].toString();
			int teacherId =  Integer.parseInt(txt.getParams()[3].toString());
			boolean res = (Boolean)ExamExecutionManager.requestTimeChange(code,time,notes, teacherId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.REQUEST_ADDITIONAL_TIME, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.LOCK_EXAM_EXECUTION)) {
			// Lock exam execution.
			String code = txt.getParams()[0].toString();
			int teacherId =  Integer.parseInt(txt.getParams()[1].toString());
			boolean res = (Boolean)ExamExecutionManager.examLock(code, teacherId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.LOCK_EXAM_EXECUTION, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.CHECK_EXISTENCE_OF_EXAM_EXECUTION_CODE)) {
			// Check if exam execution code exists.
			String code = txt.getParams()[0].toString();
			boolean res = (Boolean)ExamExecutionManager.checkExistenceOfCode(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.CHECK_EXISTENCE_OF_EXAM_EXECUTION_CODE, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_STUDENTS_WHICH_GRADE_NOT_APPROVED)) {
			// Get students which grade not approved by code .
			String code = txt.getParams()[0].toString();
			Integer[] res = ExamExecutionManager.getStudentsList(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STUDENTS_WHICH_GRADE_NOT_APPROVED, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_STUDENTS_WHICH_GRADE_IS_APPROVED)) {
			//Get students which grade is approved.		
			Integer[] res = ReportManager.getListOfStudentThatApproved();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STUDENTS_WHICH_GRADE_IS_APPROVED, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_STUDENT_GRADE)) {
			//Get students grade.	
			int studentId = Integer.parseInt(txt.getParams()[0].toString());
			Integer[] res = ReportManager.grtStudentGrades(studentId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STUDENT_GRADE, res});
		}///////

		else if(txt.getCommand().equals(CommandMessage.GET_AUTO_EXAM_ID_LIST)) {
			//Get auto exam id's list.	
			Integer[] res = ReportManager.getAutoExamIdList();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_AUTO_EXAM_ID_LIST, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_COURSE_NAME)) {
			//Get  course name by course id.
			int cid = Integer.parseInt(txt.getParams()[0].toString());
			String res = ReportManager.getCourseName(cid);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_COURSE_NAME, res});
		}///////

		else if(txt.getCommand().equals(CommandMessage.GET_AUTO_EXAM_ID_LIST)) {
			//Get auto exam id's list.	
			Integer[] res = ReportManager.getAutoExamIdList();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_AUTO_EXAM_ID_LIST, res});
		}///////
		else if(txt.getCommand().equals(CommandMessage.GET_COURSE_NAME)) {
			//Get  course name by course id.
			int cid = Integer.parseInt(txt.getParams()[0].toString());
			String res = ReportManager.getCourseName(cid);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_COURSE_NAME, res});
		}///////

		else if(txt.getCommand().equals(CommandMessage.ADD_EXAM_EXECUTION)) {
			// Add new exam execution.
			if (txt.getParams()[0] instanceof AutoExamExecution) {
				// Computerized exam execution.
				AutoExamExecution ae = (AutoExamExecution) txt.getParams()[0];

				int res = 1;
				// Check code.
				if (ExamExecutionManager.checkExistenceOfCode(ae.getCode())) {
					// Already exists cannot add.
					res = -1;
				}
				else {
					// Add.
					if (!ExamExecutionManager.addExamExecution(ae))
						res = 0;
				}
				return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ADD_EXAM_EXECUTION, res});
			}
			else if (txt.getParams()[0] instanceof ManualExamExecution) {
				// Manual exam execution.
				ManualExamExecution me = (ManualExamExecution) txt.getParams()[0];

				// Create folder if not exists.
				new File(System.getProperty("user.dir") + "\\AES").mkdirs();

				int res = 1;
				// Check code.
				if (ExamExecutionManager.checkExistenceOfCode(me.getCode())) {
					// Already exists cannot add.
					res = -1;
				}
				else {
					// Store file.
					try {
						FileOutputStream fos = new FileOutputStream(System.getProperty("user.dir") + "\\AES\\" + me.getCode() + "_" + me.getFileName());
						fos.write(me.getFileData());
						fos.close();
						me.setFileName(me.getCode() + "_" + me.getFileName());
						// Add.
						if (!ExamExecutionManager.addExamExecution(me))
							res = 0;
					}
					catch(FileNotFoundException ex) {
						notifyObservers("FileNotFoundException : " + ex);
						res = 0;
					}
					catch(IOException ioe) {
						notifyObservers("IOException : " + ioe);
						res = 0;
					}
				}
				return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.ADD_EXAM_EXECUTION, res});
			}
			return null;
		}
		else if(txt.getCommand().equals(CommandMessage.START_EXAM_EXECUTION)) {
			// Start exam execution.
			String code = txt.getParams()[0].toString();
			int StudentId = Integer.parseInt(txt.getParams()[1].toString());
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.START_EXAM_EXECUTION, ExamExecutionManager.StartExam(code, StudentId)});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_STUDENT_EXAM)) {
			// Get student's exam.
			String code = txt.getParams()[0].toString();
			int studentId = Integer.parseInt(txt.getParams()[1].toString());
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STUDENT_EXAM, ExamExecutionManager.getStudentExam(code, studentId)});
		}//////
		else if(txt.getCommand().equals(CommandMessage.GET_STATUS_OF_EXAM_EXECUTION)) {
			// Get status of exam execution.
			String code = txt.getParams()[0].toString();
			int teacherId = Integer.parseInt(txt.getParams()[1].toString());
			int res = ExamCheckingManager.getStatusOfExecution(code, teacherId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STATUS_OF_EXAM_EXECUTION, res});
		}///////
		else if (txt.getCommand().equals(CommandMessage.GET_EXECUTED_STUDENT_EXAMS)) {
			// Get executed student exams.
			String code = txt.getParams()[0].toString();
			StudentExam[] exams = ExamExecutionManager.getStudentExams(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXECUTED_STUDENT_EXAMS, exams});
		}
		else if (txt.getCommand().equals(CommandMessage.SUBMIT_STUDENT_EXAM)) {
			// Submit student answers.
			StudentExam se = (StudentExam)txt.getParams()[0];
			boolean hf = Boolean.parseBoolean(txt.getParams()[1].toString());
			if (se.getExam() instanceof AutoExamExecution) {
				// Automatic exam.
				return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.SUBMIT_STUDENT_EXAM, ExamExecutionManager.submitStudentAnswers(se, hf)});
			}
			else if (se.getExam() instanceof ManualExamExecution) {
				// Manual exam.

				ManualExamExecution me = (ManualExamExecution) se.getExam();
				// Store answers file.
				try {
					// Create folder if not exists.
					new File(System.getProperty("user.dir") + "\\AES\\Answers\\" + me.getCode()).mkdirs();

					String[] tmp = me.getFileName().trim().split("\\.");
					FileOutputStream fos = new FileOutputStream(System.getProperty("user.dir") + "\\AES\\Answers\\" + me.getCode() + "\\" + se.getStudentId() + "." + tmp[tmp.length-1]);
					fos.write(me.getAnswersFileData());
					fos.close();
				}
				catch(FileNotFoundException ex) {
					notifyObservers("FileNotFoundException : " + ex);
					return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.SUBMIT_STUDENT_EXAM, -1});
				}
				catch(IOException ioe) {
					notifyObservers("IOException : " + ioe);
					return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.SUBMIT_STUDENT_EXAM, -1});
				}

				
				return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.SUBMIT_STUDENT_EXAM, ExamExecutionManager.submitStudentAnswers(se, hf)});
			}
			return null;
		}
		else if(txt.getCommand().equals(CommandMessage.GET_TEACHER_REPORT)){
			// Retrieve notes by execution code.
			int teacherID  = Integer.parseInt(txt.getParams()[0].toString());
			ArrayList<Integer[]>  res = ExamExecutionManager.getExamList(teacherID);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_TEACHER_REPORT, res});

		}
		else if (txt.getCommand().equals(CommandMessage.CONFIRM_STUDENT_GRADE)) {
			// Confirm student's grade.
			if (txt.getParams().length < 3) {
				// Missing parameters.
				return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.CONFIRM_STUDENT_GRADE, false});
			}
			String code = txt.getParams()[0].toString();
			int studentId = Integer.parseInt(txt.getParams()[1].toString());
			int grade = Integer.parseInt(txt.getParams()[2].toString());
			String notes = txt.getParams()[3].toString();
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.CONFIRM_STUDENT_GRADE, ExamCheckingManager.confirmStudentGrade(code, studentId, grade, notes)});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_EXAM_EXECUTIONS)) {
			// Retrieve student exam executions report.
			int studentId = Integer.parseInt(txt.getParams()[0].toString());

			StudentExam[] exams = ExamExecutionManager.getStudentExamExecutions(studentId);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAM_EXECUTIONS, exams});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_EXAM_EXECUTION_REPORT)) {
			// Get executed student exams.
			int code = Integer.parseInt(txt.getParams()[0].toString());
			String[] info = ExamExecutionManager.getExamInfo(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_EXAM_EXECUTION_REPORT, info});
		}
		else if (txt.getCommand().equals(CommandMessage.VERIFY_EXAM_INFO)) {
			// Get executed student exams.
			int code = Integer.parseInt(txt.getParams()[0].toString());
			boolean bool = ReportManager.verifyExamInfo(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.VERIFY_EXAM_INFO, bool});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_STATISTICS_REPORT)) {
			// Get statistics report.
			String type = txt.getParams()[0].toString();
			int code = Integer.parseInt(txt.getParams()[1].toString());

			StatisticReportItem report = null;
			if (type.equals("COURSE"))
				report = ReportManager.getCoursesStatisticReport(code);
			else if (type.equals("TEACHER"))
				report = ReportManager.getTeacherStatisticReport(code);
			else if (type.equals("STUDENT"))
				report = ReportManager.getStudentStatisticReport(code);
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STATISTICS_REPORT, type, report});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_TEACHERS)) {
			// Get teachers list.
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_TEACHERS, UserManager.GetTeachers()});
		}
		else if (txt.getCommand().equals(CommandMessage.GET_STUDENTS)) {
			// Get students list.
			return new CommandMessage(CommandMessage.ACKNOWLEDGE_MESSAGE, new Object[] {CommandMessage.GET_STUDENTS, UserManager.GetStudents()});
		}
		else
			return new TextMessage("Error");
	}


	/**
	 * Entry point 
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		// Create two objects: Server, ServerUI
		Server s = new Server(DEFAULT_PORT);
		ServerUI ui = new ServerUI();
		// Add observer to server
		s.addObserver(ui);
		// Show UI
		ui.setVisible(true);

		// Start listening
		try {
			s.listen();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Timer tick event handler.
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		// Check Exams.
		ExamExecutionManager.checkExams();
		// Close Exams.
		ExamExecutionManager.closeExams();
	}

}
